{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [java 泛型详解](https://blog.csdn.net/s10461/article/details/53941091)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概述\n",
    "泛型在java中有很重要的地位，在面向对象编程及各种设计模式中有非常广泛的应用。\n",
    "\n",
    "什么是泛型？为什么要使用泛型？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**泛型，即“参数化类型”。一提到参数，最熟悉的就是定义方法时有形参，然后调用此方法时传递实参。那么参数化类型怎么理解呢？顾名思义，就是将类型由原来的具体的类型参数化，类似于方法中的变量参数，此时类型也定义成参数形式（可以称之为类型形参），然后在使用/调用时传入具体的类型（类型实参）。**\n",
    "\n",
    "**泛型的本质是为了参数化类型（在不创建新的类型的情况下，通过泛型指定的不同类型来控制形参具体限制的类型）**。也就是说在泛型使用过程中，操作的数据类型被指定为一个参数，这种参数类型可以用在类、接口和方法中，分别被称为`泛型类`、`泛型接口`、`泛型方法`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一个栗子\n",
    "ArrayList可以存放任意类型，可以使用范型指定特定的类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "范型测试# item=anlzou\n",
      "范型测试# item=100\n"
     ]
    }
   ],
   "source": [
    "List arrayList = new ArrayList();\n",
    "arrayList.add(\"anlzou\");\n",
    "arrayList.add(100);\n",
    "\n",
    "for(int i = 0; i < arrayList.size(); i++){\n",
    "    Object item = arrayList.get(i);\n",
    "    System.out.println(\"范型测试# \"+\"item=\" + item);\n",
    "}\n",
    "\n",
    "// List<String> arrayList = new ArrayList<String>();\n",
    "// 该范型只能存储 String 类型数据，存放其它类型报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 特性\n",
    "**泛型只在编译阶段有效。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "泛型测试# 类型相同\n"
     ]
    }
   ],
   "source": [
    "List<String> stringArrayList = new ArrayList<String>();\n",
    "List<Integer> integerArrayList = new ArrayList<Integer>();\n",
    "\n",
    "Class classStringArrayList = stringArrayList.getClass();\n",
    "Class classIntegerArrayList = integerArrayList.getClass();\n",
    "\n",
    "if(classStringArrayList.equals(classIntegerArrayList)){\n",
    "    System.out.println(\"泛型测试# \"+\"类型相同\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过上面的例子可以证明，在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型，只在编译阶段有效。在编译过程中，正确检验泛型结果后，会将泛型的相关信息擦出，并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说，泛型信息不会进入到运行时阶段。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 泛型的使用\n",
    "泛型有三种使用方式，分别为：`泛型类`、`泛型接口`、`泛型方法`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型类\n",
    "泛型类型用于类的定义中，被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类，如：List、Set、Map。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "//此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型\n",
    "//在实例化泛型类时，必须指定T的具体类型\n",
    "public class Generic<T>{\n",
    "    private T key;          //key这个成员变量的类型为T,T的类型由外部指定 \n",
    "    \n",
    "    public Generic(T key){  //泛型构造方法形参key的类型也为T，T的类型由外部指定\n",
    "        this.key = key;\n",
    "    }\n",
    "    \n",
    "    public T getKey(){      //泛型方法getKey的返回值类型为T，T的类型由外部指定\n",
    "        return key;\n",
    "    }\n",
    "    \n",
    "    public void setKey(T key){\n",
    "        this.key = key;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意：**\n",
    "1. 泛型的类型参数只能是类类型（包括自定义类），不能是简单类型。\n",
    "2. 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的，编译时会出错。\n",
    "```java\n",
    "if(ex_num instanceof Generic<Number>){\n",
    "    //\n",
    "} \n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12345\n",
      "anlzou\n",
      "99\n"
     ]
    }
   ],
   "source": [
    "//传入的实参类型需与泛型的类型参数类型相同，即为Integer\n",
    "Generic<Integer> genericInteger = new Generic(12345);\n",
    "//传入的实参类型需与泛型的类型参数类型相同，即为String\n",
    "Generic<String> genericString = new Generic(\"anlzou\");\n",
    "\n",
    "System.out.println(genericInteger.getKey());\n",
    "System.out.println(genericString.getKey());\n",
    "\n",
    "genericInteger.setKey(99);\n",
    "System.out.println(genericInteger.getKey());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型接口\n",
    "泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "//定义一个泛型接口\n",
    "public interface Generator<T>{\n",
    "    public T getKey();\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "123\n"
     ]
    }
   ],
   "source": [
    "/**\n",
    " * 未传入泛型实参时，与泛型类的定义相同，在声明类的时候，需将泛型的声明也一起加到类中\n",
    " * 即：class FruitGenerator<T> implements Generator<T>{\n",
    " * 如果不声明泛型，如：class FruitGenerator implements Generator<T>，编译器会报错：\"Unknown class\"\n",
    " */\n",
    " class FruitGenerator<T> implements Generator<T>{\n",
    "     private T key;\n",
    "     \n",
    "     public FruitGenerator(T key){\n",
    "         this.key = key;\n",
    "     }\n",
    "     \n",
    "     @Override\n",
    "     public T getKey(){\n",
    "         return this.key;\n",
    "     }\n",
    " }\n",
    " \n",
    " FruitGenerator<Integer> fruitGenerator = new FruitGenerator(123);\n",
    " System.out.println(fruitGenerator.getKey());"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Banana\n"
     ]
    }
   ],
   "source": [
    "/**\n",
    " * 传入泛型实参时：\n",
    " * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>\n",
    " * 但是我们可以为T传入无数个实参，形成无数种类型的Generator接口。\n",
    " * 在实现类实现泛型接口时，如已将泛型类型传入实参类型，则所有使用泛型的地方都要替换成传入的实参类型\n",
    " * 即：Generator<T>，public T next();中的的T都要替换成传入的String类型。\n",
    " */\n",
    " public class FruitGenerator implements Generator<String>{\n",
    "     private String[] fruits = new String[]{\"Apple\", \"Banana\", \"Pear\"};\n",
    "     \n",
    "     @Override\n",
    "     public String getKey(){\n",
    "         Random rand = new Random();\n",
    "         return fruits[rand.nextInt(3)];\n",
    "     }\n",
    " }\n",
    " \n",
    " FruitGenerator fruitGenerator = new FruitGenerator();\n",
    " System.out.println(fruitGenerator.getKey());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 补充：@Override\n",
    "@Override是伪代码,表示重写。(当然不写@Override也可以)，不过写上有如下好处: \n",
    "1. 可以当注释用,方便阅读；\n",
    "2. 编译器可以给你验证@Override下面的方法名是否是你父类中所有的，如果没有则报错。例如，你如果没写@Override，而你下面的方法名又写错了，这时你的编译器是可以编译通过的，因为编译器以为这个方法是你的子类中自己增加的方法。\n",
    "\n",
    "nextInt(n)将返回一个大于等于0小于n的随机数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型通配符\n",
    "我们知道`Ingeter`是`Number`的一个子类，同时在特性章节中我们也验证过`Generic<Ingeter>`与`Generic<Number>`实际上是相同的一种基本类型。那么问题来了，在使用`Generic<Number>`作为形参的方法中，能否使用`Generic<Ingeter>`的实例传入呢？在逻辑上类似于`Generic<Number>`和`Generic<Ingeter>`是否可以看成具有父子关系的泛型类型呢？\n",
    "\n",
    "为了弄清楚这个问题，我们使用`Generic<T>`这个泛型类继续看下面的例子:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "public void showKeyValue_1(Generic<Number> obj){\n",
    "    System.out.println(\"范型测试# \"+\"key value is\"+obj.getKey());\n",
    "}\n",
    "\n",
    "Generic<Integer> gInteger = new Generic(123);\n",
    "Generic<Number> gNumber = new Generic(456);\n",
    "\n",
    "//showKeyValue(gNumber);\n",
    "// showKeyValue这个方法编译器会为我们报错：Generic<java.lang.Integer> \n",
    "// cannot be applied to Generic<java.lang.Number>\n",
    "// showKeyValue(gInteger);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过提示信息我们可以看到`Generic<Integer>`不能被看作为`Generic<Number>`的子类。由此可以看出: **同一种泛型可以对应多个版本（因为参数类型是不确定的），不同版本的泛型类实例是不兼容的。**\n",
    "\n",
    "回到上面的例子，如何解决上面的问题？总不能为了定义一个新的方法来处理`Generic<Integer>`类型的类，这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示**同时**是`Generic<Integer>`和`Generic<Number>`父类的引用类型。由此类型通配符应运而生。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "范型测试# key value is:123\n",
      "范型测试# key value is:456\n"
     ]
    }
   ],
   "source": [
    "public void showKeyValue(Generic<?> obj){\n",
    "    System.out.println(\"范型测试# \"+\"key value is:\"+obj.getKey());\n",
    "}\n",
    "\n",
    "Generic<Integer> gInteger = new Generic(123);\n",
    "Generic<Number> gNumber = new Generic(456);\n",
    "\n",
    "showKeyValue(gInteger);\n",
    "showKeyValue(gNumber);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类型通配符一般是使用`?`代替具体的类型实参，注意了，**此处`?`是类型实参，而不是类型形参 。**  \n",
    "再直白点的意思就是，此处的？和Number、String、Integer一样都是一种实际的类型，可以把？看成所有类型的父类。是一种真实的类型。  \n",
    "可以解决当具体类型不确定的时候，这个通配符就是` ?  `；当操作类型时，不需要使用类型的具体功能时，只使用Object类中的功能。那么可以用` ? `通配符来表未知类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型方法\n",
    "**泛型类，是在实例化类的时候指明泛型的具体类型；泛型方法，是在调用方法的时候指明泛型的具体类型 。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "ename": "EvalException",
     "evalue": "packages.anlzou.Test",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1m\u001b[31mjava.lang.ClassNotFoundException: packages.anlzou.Test\u001b[0m",
      "\u001b[1m\u001b[31m\tat java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)\u001b[0m",
      "\u001b[1m\u001b[31m\tat jdk.jshell/jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader.findClass(DefaultLoaderDelegate.java:154)\u001b[0m",
      "\u001b[1m\u001b[31m\tat java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)\u001b[0m",
      "\u001b[1m\u001b[31m\tat java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)\u001b[0m",
      "\u001b[1m\u001b[31m\tat java.base/java.lang.Class.forName0(Native Method)\u001b[0m",
      "\u001b[1m\u001b[31m\tat java.base/java.lang.Class.forName(Class.java:315)\u001b[0m",
      "\u001b[1m\u001b[31m\tat .(#80:1)\u001b[0m"
     ]
    }
   ],
   "source": [
    "/**\n",
    " * 泛型方法的基本介绍\n",
    " * @param tClass 传入的泛型实参\n",
    " * @return T 返回值为T类型\n",
    " * 说明：\n",
    " *     1）public 与 返回值中间<T>非常重要，可以理解为声明此方法为泛型方法。\n",
    " *     2）只有声明了<T>的方法才是泛型方法，泛型类中的使用了泛型的成员方法并不是泛型方法。\n",
    " *     3）<T>表明该方法将使用泛型类型T，此时才可以在方法中使用泛型类型T。\n",
    " *     4）与泛型类的定义一样，此处T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型。\n",
    " */\n",
    " \n",
    " public <T> T genericMethod(Class<T> tClass) throws InstantiationException, IllegalAccessException{\n",
    "     T instance = tClass.newInstance();\n",
    "     return instance;\n",
    " }\n",
    " \n",
    " Object obj = genericMethod(Class.forName(\"packages.anlzou.Test\"));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 泛型方法的基本用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "123"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "//这个类是个泛型类\n",
    "public class Generic<T>{\n",
    "    private T key;\n",
    "\n",
    "    public Generic(T key){\n",
    "        this.key = key;\n",
    "    }\n",
    "\n",
    "    /**\n",
    "     * 虽然在方法中使用了泛型，但是这并不是一个泛型方法。\n",
    "     * 这只是类中一个普通的成员方法，只不过他的返回值是在声明泛型类已经声明过的泛型。\n",
    "     * 所以在这个方法中才可以继续使用 T 这个泛型。\n",
    "     */\n",
    "    public T getKey(){\n",
    "        return key;\n",
    "    }\n",
    "}\n",
    "\n",
    "Generic<Integer> generic = new Generic(123);\n",
    "generic.getKey();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "container key: 123\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "123"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "/**\n",
    " * 这才是一个真正的泛型方法。\n",
    " * 首先在public与返回值之间的<T>必不可少，这表明这是一个泛型方法，并且声明了一个泛型T\n",
    " * 这个T可以出现在这个泛型方法的任意位置.\n",
    "*/\n",
    "public <T> T showKeyName(Generic<T> container){\n",
    "    System.out.println(\"container key: \"+container.getKey());\n",
    "    T test = container.getKey();\n",
    "    return test;\n",
    "}\n",
    "\n",
    "showKeyName(generic);\n",
    "\n",
    "// 泛型的数量也可以为任意多个 \n",
    "//public <T,K> K showKeyName(Generic<T> container){\n",
    "//  ...\n",
    "// }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "泛型测试# key value is 456\n"
     ]
    }
   ],
   "source": [
    "//这也不是一个泛型方法，这就是一个普通的方法，只是使用了Generic<Number>这个泛型类做形参而已。\n",
    "public void showKeyValue1(Generic<Number> obj){\n",
    "   System.out.println(\"泛型测试# \"+\"key value is \" + obj.getKey());\n",
    "}\n",
    "\n",
    "Generic<Number> generic1 = new Generic(456);\n",
    "showKeyValue1(generic1);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "泛型测试# key value is 111\n",
      "泛型测试# key value is anlzou\n"
     ]
    }
   ],
   "source": [
    "//这也不是一个泛型方法，这也是一个普通的方法，只不过使用了泛型通配符?\n",
    "//同时这也印证了泛型通配符章节所描述的，?是一种类型实参，可以看做为Number等所有类的父类\n",
    "public void showKeyValue2(Generic<?> obj){\n",
    "    System.out.println(\"泛型测试# \"+\"key value is \" + obj.getKey());\n",
    "}\n",
    "\n",
    "Generic<Number> genericNum = new Generic(111);\n",
    "showKeyValue2(genericNum);\n",
    "\n",
    "Generic<String> genericStr = new Generic(\"anlzou\");\n",
    "showKeyValue2(genericStr);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类中的泛型方法\n",
    "当然这并不是泛型方法的全部，泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的，当泛型方法出现在泛型类中时。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class GenericFruit{\n",
    "    class Fruit{\n",
    "        @Override\n",
    "        public String toString(){\n",
    "            return \"fruit\";\n",
    "        }\n",
    "    }\n",
    "    \n",
    "    class Apple extends Fruit{\n",
    "        @Override\n",
    "        public String toString(){\n",
    "            return \"apple\";\n",
    "        }\n",
    "    }\n",
    "    \n",
    "    class Person{\n",
    "        @Override\n",
    "        public String toString(){\n",
    "            return \"Persion\";\n",
    "        }\n",
    "    }\n",
    "    \n",
    "    class GenerateTest<T>{\n",
    "        public void show_1(T t){\n",
    "            System.out.println(t.toString());\n",
    "        }\n",
    "    \n",
    "        //在泛型类中声明了一个泛型方法，使用泛型T，注意这个T是一种全新的类型，可以与泛型类中声明的T不是同一种类型。\n",
    "        public <T> void show_2(T t){\n",
    "            System.out.println(t.toString());\n",
    "        }\n",
    "\n",
    "        //在泛型类中声明了一个泛型方法，使用泛型E，这种泛型E可以为任意类型。可以类型与T相同，也可以不同。\n",
    "        //由于泛型方法在声明的时候会声明泛型<E>，因此即使在泛型类中并未声明泛型，编译器也能够正确识别泛型方法中识别的泛型。\n",
    "        public <E> void show_3(E t){\n",
    "            System.out.println(t.toString());\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fruit\n",
      "apple\n",
      "Persion\n",
      "anlzou\n",
      "fruit\n",
      "apple\n",
      "apple\n",
      "Persion\n",
      "apple\n",
      "Persion\n"
     ]
    }
   ],
   "source": [
    "GenericFruit genericFruit = new GenericFruit();\n",
    "GenericFruit.Fruit fruit = genericFruit.new Fruit(); \n",
    "System.out.println(fruit.toString());\n",
    "\n",
    "GenericFruit.Apple apple = genericFruit.new Apple();\n",
    "System.out.println(apple.toString());\n",
    "\n",
    "GenericFruit.Person person = genericFruit.new Person();\n",
    "System.out.println(person.toString());\n",
    "\n",
    "GenericFruit.GenerateTest<String> generateTest = genericFruit.new GenerateTest();\n",
    "generateTest.show_1(\"anlzou\");\n",
    "\n",
    "GenericFruit.GenerateTest<GenericFruit.Fruit> generateTest = genericFruit.new GenerateTest();\n",
    "//apple是Fruit的子类，所以这里可以\n",
    "generateTest.show_1(fruit);\n",
    "generateTest.show_1(apple);\n",
    "//编译器会报错，因为泛型类型实参指定的是Fruit，而传入的实参类是Person\n",
    "//generateTest.show_1(person);\n",
    "\n",
    "//使用这两个方法都可以成功\n",
    "generateTest.show_2(apple);\n",
    "generateTest.show_2(person);\n",
    "\n",
    "//使用这两个方法也都可以成功\n",
    "generateTest.show_3(apple);\n",
    "generateTest.show_3(person);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 补充：创建内部类对象\n",
    "创建静态内部类对象的一般形式为：  \n",
    "外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()\n",
    "\n",
    "创建成员内部类对象的一般形式为：  \n",
    "外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型方法与可变参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# t is 111\n",
      "# t is 222\n",
      "# t is aaaa\n",
      "# t is 2323.4\n",
      "# t is 55.55\n"
     ]
    }
   ],
   "source": [
    "public <T> void printMsg(T...args){\n",
    "    for(T t:args){\n",
    "        System.out.println(\"# \" + \"t is \" + t);\n",
    "    }\n",
    "}\n",
    "\n",
    "printMsg(\"111\",222,\"aaaa\",\"2323.4\",55.55);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 静态方法与泛型\n",
    "静态方法有一种情况需要注意一下，那就是在类中的静态方法使用泛型：**静态方法无法访问类上定义的泛型(变量&方法)；如果静态方法操作的引用数据类型不确定的时候，必须要将泛型定义在方法上。**\n",
    "\n",
    "即：**如果静态方法要使用泛型的话，必须将静态方法也定义成泛型方法 。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 136,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class StaticGenerator<T>{\n",
    "    /**\n",
    "     * 如果在类中定义使用泛型的静态方法，需要添加额外的泛型声明（将这个方法定义成泛型方法）\n",
    "     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。\n",
    "     * 如：public static void show(T t){..},此时编译器会提示错误信息：\n",
    "     *     \"StaticGenerator cannot be refrenced from static context\"\n",
    "     */\n",
    "     public static <T> void show(T t){\n",
    "         System.out.println(\"test\");\n",
    "     }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 泛型方法总结\n",
    "泛型方法能使方法独立于类而产生变化，以下是一个基本的指导原则：\n",
    "\n",
    "```\n",
    "无论何时，如果你能做到，你就该尽量使用泛型方法。也就是说，如果使用泛型方法将整个类泛型化，那么就应该使用泛型方法。另外对于一个static的方法而已，无法访问泛型类型的参数。所以如果static方法要使用泛型能力，就必须使其成为泛型方法。\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 泛型上下边界\n",
    "在使用泛型的时候，我们还可以为传入的泛型类型实参进行上下边界的限制，如：类型实参只准传入某种类型的父类或某种类型的子类。\n",
    "- 上限：? extends E：可以接收E类型或者E的子类型对象。\n",
    "- 下限：? super E：可以接收E类型或者E的父类型对象。\n",
    "\n",
    "上限什么时候用：往集合中添加元素时，既可以添加E类型对象，又可以添加E的子类型对象。为什么？因为取的时候，E类型既可以接收E类对象，又可以接收E的子类型对象。\n",
    "\n",
    "下限什么时候用：当从集合中获取元素进行操作的时候，可以用当前元素的类型接收，也可以用当前元素的父类型接收。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "范型测试# key value is 2222\n",
      "范型测试# key value is 2.4\n",
      "范型测试# key value is 2.56\n",
      "范型测试# key value is 2222\n",
      "范型测试# key value is 99.9\n"
     ]
    }
   ],
   "source": [
    "public void showKeyValue1(Generic<? extends Number> obj){\n",
    "    System.out.println(\"范型测试# \" + \"key value is \" + obj.getKey());\n",
    "}\n",
    "\n",
    "public void showKeyValue2(Generic<? super Integer> obj){\n",
    "    System.out.println(\"范型测试# \" + \"key value is \" + obj.getKey());\n",
    "}\n",
    "\n",
    "//这一行代码编译器会提示错误，因为String类型并不是Number类型的子类\n",
    "//Generic<String> generic1 = new Generic<String>(\"11111\");\n",
    "Generic<Integer> generic2 = new Generic<Integer>(2222);\n",
    "Generic<Float> generic3 = new Generic<Float>(2.4f);\n",
    "Generic<Double> generic4 = new Generic<Double>(2.56);\n",
    "Generic<Number> generic5 = new Generic<Number>(99.9);\n",
    "//报错\n",
    "//Generic<Object> generic6 = new Generic<Object>(\"1111\");\n",
    "\n",
    "//这一行代码编译器会提示错误，因为String类型并不是Number类型的子类\n",
    "//showKeyValue1(generic1);\n",
    "\n",
    "showKeyValue1(generic2);\n",
    "showKeyValue1(generic3);\n",
    "showKeyValue1(generic4);\n",
    "\n",
    "showKeyValue2(generic2);\n",
    "showKeyValue2(generic5);\n",
    "//showKeyValue2(generic6);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果我们把泛型类的定义也改一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class Generic<T extends Number>{\n",
    "    private T key;\n",
    "\n",
    "    public Generic(T key) {\n",
    "        this.key = key;\n",
    "    }\n",
    "\n",
    "    public T getKey(){\n",
    "        return key;\n",
    "    }\n",
    "}\n",
    "\n",
    "//这一行代码也会报错，因为String不是Number的子类\n",
    "//Generic<String> generic1 = new Generic<String>(\"11111\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "再来一个泛型方法的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {},
   "outputs": [],
   "source": [
    "//在泛型方法中添加上下边界限制的时候，必须在权限声明与返回值之间的<T>上添加上下边界，即在泛型声明的时候添加\n",
    "//public <T> T showKeyName(Generic<T extends Number> container)，编译器会报错：\"Unexpected bound\"\n",
    "public <T extends Number> T showKeyName(Generic<T> container){\n",
    "    System.out.println(\"container key :\" + container.getKey());\n",
    "    T test = container.getKey();\n",
    "    return test;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 关于泛型数组\n",
    "说明文档在java中是\"**不能创建一个确切的泛型类型的数组**\"的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 对 使用通配符创建泛型数组是可以的\n",
    "List<?>[] ls = new ArrayList<?>[10];"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 对\n",
    "List<String>[] ls = new ArrayList[10];"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 错\n",
    "//List<String>[] ls = new ArrayList<String>[10];"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面采用通配符的方式是被允许的:**数组的类型不可以是类型变量，除非是采用通配符的方式**，因为对于通配符的方式，最后取出数据是要做显式的类型转换的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "null\n",
      "[3]\n",
      "[3]\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    \n",
    "Object o = lsa;    \n",
    "Object[] oa = (Object[]) o;    \n",
    "List<Integer> li = new ArrayList<Integer>();    \n",
    "li.add(new Integer(3));    \n",
    "oa[1] = li; // Correct.    \n",
    "Integer i = (Integer) lsa[1].get(0); // OK \n",
    "\n",
    "System.out.println(lsa[0]);\n",
    "System.out.println(li);\n",
    "System.out.println(oa[1]);\n",
    "System.out.println(i);"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "11.0.8+10-post-Ubuntu-0ubuntu120.04"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "213.95px"
   },
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
